home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / Tool Chest / Development Kits / MPW etc / MPW-GM / MPW / Examples / PPCExamples / CPlusExamples / CPlusTESample / TEDocument.cp < prev    next >
Encoding:
Text File  |  1998-12-03  |  31.8 KB  |  1,067 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple TextEdit Sample Application
  6. #
  7. #    CPlusTESample
  8. #
  9. #    TEDocument.cp    -    C++ source
  10. #
  11. #    Copyright © 1989 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    
  15. #            1.10                     07/89
  16. #            1.00                     04/89
  17. #
  18. #    Components:
  19. #            CPlusTESample.make        July 9, 1989
  20. #            TApplicationCommon.h    July 9, 1989
  21. #            TApplication.h            July 9, 1989
  22. #            TDocument.h                July 9, 1989
  23. #            TECommon.h                July 9, 1989
  24. #            TESample.h                July 9, 1989
  25. #            TEDocument.h            July 9, 1989
  26. #            TApplication.cp            July 9, 1989
  27. #            TDocument.cp            July 9, 1989
  28. #            TESample.cp                July 9, 1989
  29. #            TEDocument.cp            July 9, 1989
  30. #            TESampleGlue.a            July 9, 1989
  31. #            TApplication.r            July 9, 1989
  32. #            TESample.r                July 9, 1989
  33. #
  34. #    CPlusTESample is an example application that demonstrates
  35. #    how to initialize the commonly used toolbox managers,
  36. #    operate successfully under MultiFinder, handle desk
  37. #    accessories and create, grow, and zoom windows. The
  38. #    fundamental TextEdit toolbox calls and TextEdit autoscroll
  39. #    are demonstrated. It also shows how to create and maintain
  40. #    scrollbar controls. 
  41. #
  42. #    This version of TESample has been substantially reworked in
  43. #    C++ to show how a "typical" object oriented program could
  44. #    be written. To this end, what was once a single source code
  45. #    file has been restructured into a set of classes which
  46. #    demonstrate the advantages of object-oriented programming.
  47. #
  48. ------------------------------------------------------------------------------*/
  49.  
  50.  
  51. /*
  52. Segmentation strategy:
  53.  
  54.     This program has only one segment, since the issues
  55.     surrounding segmentation within a class's methods have
  56.     not been investigated yet. We DO unload the data
  57.     initialization segment at startup time, which frees up
  58.     some memory 
  59.  
  60. SetPort strategy:
  61.  
  62.     Toolbox routines do not change the current port. In
  63.     spite of this, in this program we use a strategy of
  64.     calling SetPort whenever we want to draw or make calls
  65.     which depend on the current port. This makes us less
  66.     vulnerable to bugs in other software which might alter
  67.     the current port (such as the bug (feature?) in many
  68.     desk accessories which change the port on OpenDeskAcc).
  69.     Hopefully, this also makes the routines from this
  70.     program more self-contained, since they don't depend on
  71.     the current port setting. 
  72.  
  73. Clipboard strategy:
  74.  
  75.     This program does not maintain a private scrap.
  76.     Whenever a cut, copy, or paste occurs, we import/export
  77.     from the public scrap to TextEdit's scrap right away,
  78.     using the TEToScrap and TEFromScrap routines. If we did
  79.     use a private scrap, the import/export would be in the
  80.     activate/deactivate event and suspend/resume event
  81.     routines. 
  82. */
  83.  
  84. // Mac Includes
  85. #include <Types.h>
  86. #include <Quickdraw.h>
  87. #include <Fonts.h>
  88. #include <Controls.h>
  89. #include <Windows.h>
  90. #include <TextEdit.h>
  91. #include <Dialogs.h>
  92. #include <Menus.h>
  93. #include <Devices.h>
  94. #include <Events.h> 
  95. #include <Scrap.h>
  96. #include <TextUtils.h>
  97. #include <MacMemory.h>
  98. #include <SegLoad.h>
  99. #include <Files.h>
  100. #include <OSUtils.h>
  101. #include <Traps.h>
  102. #include <LowMem.h>
  103.  
  104. #include "TEDocument.h"
  105. // we need resource definitions
  106. #include "TECommon.h"
  107.  
  108. // #include "TESample.h"
  109.  
  110. extern "C"
  111.     // prototypes for functions that don't belong to any one class
  112.         pascal     TEClickLoopUPP GetOldClickLoop(void);
  113.         pascal     void PascalClickLoop(void);
  114.         void     CommonAction(ControlHandle control,short* amount);
  115.         pascal     void VActionProc(ControlHandle control,short part);
  116.         pascal     void HActionProc(ControlHandle control,short part);
  117.     
  118.     
  119.     // ASMCLICKLOOP is written in Assembler for the 68K, since it needs to tweak
  120.     // registers.  For the PowerPC, we can write this routine in C because 
  121.     // MixedMode gives us a C interface for this.
  122.     
  123.        pascal Boolean ASMCLICKLOOP (TEPtr pTE);
  124.  
  125. };
  126.  
  127. // kTextMargin is the number of pixels we leave blank at the edge of the window.
  128.     const short kTextMargin = 2;
  129.  
  130. // kMaxDocWidth is an arbitrary number used to specify the width of the TERec's
  131. // destination rectangle so that word wrap and horizontal scrolling can be
  132. // demonstrated.
  133.     const short    kMaxDocWidth = 576;
  134.     
  135. // kMinDocDim is used to limit the minimum dimension of a window when GrowWindow
  136. // is called.
  137.     const short    kMinDocDim = 64;
  138.     
  139. // kMaxTELength is an arbitrary number used to limit the length of text in the TERec
  140. // so that various errors won't occur from too many characters being in the text.
  141.     const short    kMaxTELength = 32000;
  142.  
  143. // kControlInvisible is used the same way to 'turn on' the control.
  144.     const short kControlVisible = 0xFF;
  145.  
  146. // ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
  147. // values for control positioning and sizing.
  148.     const short kScrollbarAdjust = 15;
  149.     const short kGrowboxAdjust = 15;
  150.     const short kScrollbarWidth = 16;
  151.  
  152. // kTESlop provides some extra security when pre-flighting edit commands.
  153.     const short kTESlop = 1024;
  154.  
  155. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  156. // to have borders coincide with the growbox.
  157.     const short kScrollTweek = 2;
  158.     
  159. // kCrChar is used to match with a carriage return when calculating the
  160. // number of lines in the TextEdit record. kDelChar is used to check for
  161. // delete in keyDowns.
  162.     const short kCrChar = 13;
  163.     const short kDelChar = 8;
  164.     
  165. // Use universal procedure pointers that are compatible with both 68K and PowerPC.
  166.  
  167.     ControlActionUPP VActionDesc;
  168.     ControlActionUPP HActionDesc;
  169.     TEClickLoopUPP   TEClickLoopDesc;
  170.     Boolean             inited = false;
  171.     
  172.  
  173. /***********************************************************************/
  174. //
  175. // TEDocument class declarations
  176. //
  177. /***********************************************************************/
  178.  
  179. //-----------------------------------------------------------------------
  180. // TEDocument::TEDocument -    notice that we pass the resID parameter up to our
  181. //                            base class, which actually creates the window for us.
  182. //                            If you modify this so that it doesn't create a
  183. //                            window, be sure to take that into account in the
  184. //                            application class so that it doesn't believe an
  185. //                            error occurred (as version 1.20 of TApplication
  186. //                            would).
  187. //
  188.     TEDocument::TEDocument( short resID )    : TDocument( resID )
  189.     {
  190.         Boolean good;
  191.         Rect destRect, viewRect;
  192.         
  193.         // initialize routine desciptors once
  194.         if (!inited)
  195.         {
  196.             VActionDesc = NewControlActionProc (VActionProc);
  197.             HActionDesc = NewControlActionProc (HActionProc);
  198.             TEClickLoopDesc = NewTEClickLoopProc (ASMCLICKLOOP);
  199.             inited = true;
  200.         }
  201.  
  202.         good = false;
  203.         SetPort( fDocWindow );
  204.         GetTERect( &viewRect );
  205.         destRect = viewRect;
  206.         destRect.right = destRect.left + kMaxDocWidth;
  207.         fDocTE = TENew( &destRect, &viewRect );
  208.         
  209.         good = fDocTE != nil;    // if TENew succeeded, we have a good document 
  210.     
  211.         // if it's a valid document, initialize the text edit fields
  212.         // and try to create a vertical scroll control
  213.             if ( good )
  214.             {
  215.                 // set up TE record
  216.                     AdjustViewRect();
  217.                     TEAutoView( true, fDocTE );
  218.                     fDocClick = (*fDocTE)->clickLoop;
  219.                     (*fDocTE)->clickLoop =  TEClickLoopDesc;
  220.  
  221.                 // get vertical scrollbar
  222.                     fDocVScroll = GetNewControl( rVScroll, fDocWindow );
  223.                     good = ( fDocVScroll != nil );
  224.             }
  225.         
  226.         // if the vertical scroll control has been properly created
  227.         // try to create a horizontal one. otherwise, don't bother
  228.             if ( good )
  229.             {
  230.                 fDocHScroll = GetNewControl( rHScroll, fDocWindow );
  231.                 good = ( fDocHScroll != nil );
  232.             }
  233.         
  234.         // If both horizontal and vertical controls have been created
  235.         // then we can go ahead and use this new document, so show it.
  236.         // Otherwise, let the user know that we have a problem.
  237.             if ( good )                
  238.             {                                                        // adjust & draw the controls, draw the window
  239.                 SetWindowDoc( fDocWindow, this );
  240.                 AdjustScrollValues( true );
  241.                 ShowWindow( fDocWindow );
  242.             }
  243.             else
  244.                 AlertUser( kTEDocErrStrings, eNoWindow );            // tell user we failed
  245.             
  246.     } /* TEDocument (constructor) */
  247.  
  248.  
  249. //-----------------------------------------------------------------------
  250. // TEDocument::~TEDocument -    At this point, if there was a document associated 
  251. //                                with a window, you could do any document saving
  252. //                                processing if it is 'dirty'. DoCloseWindow would
  253. //                                return true if the window actually closed, i.e.,
  254. //                                the user didn’t cancel from a save dialog. This
  255. //                                result is handy whenthe user quits an application,
  256. //                                but then cancels the save of a documentassociated
  257. //                                with a window.
  258. //
  259.     TEDocument::~TEDocument(void)
  260.     {
  261.         HideWindow( fDocWindow );
  262.         
  263.         // get rid of any extra storage that was used for the window
  264.             if ( fDocTE != nil )
  265.                 TEDispose(fDocTE);                                    // dispose the TEHandle if we got far enough to make one 
  266.         
  267.             if ( fDocVScroll != nil )                                // don't forget about the control storage
  268.                 DisposeControl(fDocVScroll);
  269.     
  270.             if ( fDocHScroll != nil )
  271.                 DisposeControl(fDocHScroll);
  272.     
  273.         // base class destructor will dispose of window
  274.     } /* TEDocument (destructor) */
  275.  
  276.  
  277. //-----------------------------------------------------------------------
  278. // TEDocument::DoZoom -     
  279. //
  280.     void TEDocument::DoZoom( short partCode )
  281.     {
  282.         Rect tRect;
  283.     
  284.         tRect = fDocWindow->portRect;
  285.         EraseRect( &tRect );
  286.         ZoomWindow( fDocWindow, partCode, fDocWindow == FrontWindow());
  287.         AdjustScrollbars(true);            // adjust, redraw anyway 
  288.         AdjustTE();
  289.         InvalRect( &tRect );            // invalidate the whole content 
  290.         
  291.         // the scrollbars were taken care of by AdjustScrollbars, so validate ’em 
  292.             tRect = (*fDocVScroll)->contrlRect;
  293.             ValidRect( &tRect );
  294.             tRect = (*fDocHScroll)->contrlRect;
  295.             ValidRect( &tRect );
  296.         
  297.     } /* TEDocument::DoZoom */
  298.  
  299.  
  300. //-----------------------------------------------------------------------
  301. // TEDocument::DoGrow -    Called when a mouseDown occurs in the grow box of
  302. //                        an active window.
  303. //
  304.     void TEDocument::DoGrow( EventRecord* theEvent )
  305.     {
  306.         long growResult;
  307.         Rect tRect, tRect2;
  308.         
  309.         tRect = qd.screenBits.bounds;
  310.         tRect.left = kMinDocDim;
  311.         tRect.top = kMinDocDim;
  312.         growResult = GrowWindow(fDocWindow, theEvent->where, &tRect);
  313.         
  314.         // see if it really changed size 
  315.             if ( growResult != 0 )
  316.             {
  317.                 tRect = (*fDocTE)->viewRect;
  318.                 SizeWindow(fDocWindow, LoWrd(growResult), HiWrd(growResult), true);
  319.                 AdjustScrollbars(true);
  320.                 AdjustTE();
  321.                 
  322.                 // calculate & validate the region that hasn’t changed so it won’t get redrawn
  323.                 // Note: we copy rectangles so that we don't take address of object fields.
  324.                     tRect2 = (*fDocTE)->viewRect;
  325.                     (void) SectRect(&tRect, &tRect2, &tRect);
  326.                     tRect2 = fDocWindow->portRect; InvalRect(&tRect2);
  327.                     ValidRect(&tRect);
  328.                     tRect2 = (*fDocVScroll)->contrlRect; ValidRect(&tRect2);
  329.                     tRect2 = (*fDocHScroll)->contrlRect; ValidRect(&tRect2);
  330.             }
  331.     } /* TEDocument::DoGrow */
  332.  
  333.  
  334. //-----------------------------------------------------------------------
  335. // TEDocument::DoContent -    
  336. //
  337.     void TEDocument::DoContent( EventRecord* theEvent )
  338.     {
  339.         Point mouse;
  340.         ControlHandle control;
  341.         short part, value;
  342.         Boolean shiftDown;
  343.         Rect teRect;
  344.     
  345.         SetPort( fDocWindow );
  346.         mouse = theEvent->where;                            // get the click position 
  347.         GlobalToLocal( &mouse );
  348.         GetTERect( &teRect );
  349.         if ( PtInRect( mouse, &teRect ))
  350.         {
  351.             /* see if we need to extend the selection */
  352.                 shiftDown = ( theEvent->modifiers & shiftKey ) != 0;    /* extend if Shift is down */
  353.                 TEClick( mouse, shiftDown, fDocTE );
  354.         }
  355.         else
  356.         {
  357.             part = FindControl( mouse, fDocWindow, &control );
  358.             switch ( part )
  359.             {
  360.                 case 0:            break;        // do nothing if not in a control
  361.                     
  362.                 case kControlIndicatorPart:
  363.                                 value = GetControlValue( control );
  364.                                 part = TrackControl( control, mouse, nil );
  365.                                 if ( part != 0 )
  366.                                 {
  367.                                     value -= GetControlValue(control);
  368.                                     
  369.                                     // value now has CHANGE in value; if value changed, scroll 
  370.                                         if ( value != 0 )
  371.                                             if ( control == fDocVScroll )
  372.                                                 TEScroll(0, value * (*fDocTE)->lineHeight, fDocTE);
  373.                                             else
  374.                                                 TEScroll(value, 0, fDocTE);
  375.                                 }
  376.                                 break;
  377.                     
  378.                 default:        if ( control == fDocVScroll )                                        // they clicked in an arrow, so track & scroll 
  379.                                     value = TrackControl( control, mouse,  VActionDesc );
  380.                                 else
  381.                                     value = TrackControl( control, mouse,  HActionDesc );
  382.                                 break;                        
  383.             }
  384.         }
  385.     } /* TEDocument::DoContent */
  386.  
  387.  
  388. //-----------------------------------------------------------------------
  389. // TEDocument::DoKeyDown -    
  390. //
  391.     void TEDocument::DoKeyDown( EventRecord* theEvent )
  392.     {
  393.         char key;
  394.     
  395.         if ( theEvent->modifiers & cmdKey )                            // don't process command characters
  396.             return;
  397.         key = (char) ( theEvent->message & charCodeMask );
  398.         
  399.         // we have a char. for our window; see if we are still below TextEdit’s
  400.         // limit for the number of characters
  401.             if (( key == kDelChar ) || ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart ) + 1 < kMaxTELength ))
  402.             {
  403.                 TEKey(key, fDocTE);
  404.                 AdjustScrollbars(false);
  405.                 AdjustTE();
  406.             }
  407.             else
  408.                 AlertUser( kTEDocErrStrings, eExceedChar );
  409.             
  410.     } /* TEDocument::DoKeyDown */
  411.  
  412.  
  413. //-----------------------------------------------------------------------
  414. // TEDocument::DoActivate -    
  415. //
  416.     void TEDocument::DoActivate( Boolean becomingActive )
  417.     {
  418.         if ( becomingActive )
  419.         {
  420.             RgnHandle    tempRgn;
  421.             RgnHandle    clipRgn;
  422.             Rect        growRect;
  423.             Rect        tRect;
  424.     
  425.             // since we don’t want TEActivate to draw a selection in an area where
  426.             // we’re going to erase and redraw, we’ll clip out the update region
  427.             // before calling it.
  428.                 tempRgn = NewRgn();
  429.                 clipRgn = NewRgn();
  430.                 
  431.             // save old update region
  432.                 CopyRgn(((WindowPeek) fDocWindow)->updateRgn, tempRgn );
  433.                 
  434.             // put it in local coords
  435.                 OffsetRgn( tempRgn, fDocWindow->portBits.bounds.left, fDocWindow->portBits.bounds.top );
  436.                 GetClip( clipRgn );
  437.                 
  438.             // subtract updateRgn from clipRgn
  439.                 DiffRgn( clipRgn, tempRgn, tempRgn );
  440.             
  441.             // make it the new clipRgn
  442.                 SetClip( tempRgn );
  443.                 TEActivate( fDocTE );
  444.                 
  445.             // restore the full-blown clipRgn
  446.                 SetClip( clipRgn );
  447.                 
  448.             // get rid of temp regions
  449.                 DisposeRgn( tempRgn );
  450.                 DisposeRgn( clipRgn );
  451.     
  452.             // the controls must be redrawn on activation
  453.                 (*fDocVScroll)->contrlVis = kControlVisible;
  454.                 (*fDocHScroll)->contrlVis = kControlVisible;
  455.                 
  456.             // copy rectangles to avoid unsafe object field references!
  457.                 tRect = (*fDocVScroll)->contrlRect; InvalRect( &tRect );
  458.                 tRect = (*fDocHScroll)->contrlRect; InvalRect( &tRect );
  459.  
  460.             // the growbox needs to be redrawn on activation:
  461.                 growRect = fDocWindow->portRect;
  462.  
  463.             // adjust for the scrollbars
  464.                 growRect.top = growRect.bottom - kScrollbarAdjust;
  465.                 growRect.left = growRect.right - kScrollbarAdjust;
  466.                 InvalRect( &growRect );
  467.         }
  468.         else
  469.         {        
  470.             TEDeactivate(fDocTE);
  471.         
  472.             // the controls must be hidden on deactivation
  473.                 HideControl(fDocVScroll);
  474.                 HideControl(fDocHScroll);
  475.         
  476.             // we draw grow icon immediately, since we deactivate controls
  477.             // immediately, and the update delay looks funny
  478.                 DrawGrowIcon(fDocWindow);
  479.         }
  480.           
  481.     } /* TEDocument::DoActivate */
  482.  
  483.  
  484. //-----------------------------------------------------------------------
  485. // TEDocument::DoUpdate -    
  486. //
  487.     void TEDocument::DoUpdate(void)
  488.     {
  489.         BeginUpdate( fDocWindow );                // this sets up the visRgn 
  490.         if ( ! EmptyRgn( fDocWindow->visRgn ))    // draw if updating needs to be done 
  491.         {
  492.             DrawWindow();
  493.         }
  494.         EndUpdate( fDocWindow );
  495.         
  496.     } /* TEDocument::DoUpdate */
  497.  
  498.  
  499. //-----------------------------------------------------------------------
  500. // TEDocument::CalcIdle -    calculate how much idle time we need
  501. //
  502.     unsigned long TEDocument::CalcIdle(void)
  503.     {
  504.         if ( HaveSelection())
  505.             return GetCaretTime();                // interval needed to blink insertion caret
  506.         else
  507.             return kMaxSleepTime;                // if we don't have a selection, we don't need to idle
  508.         
  509.     } /* TEDocument::CalcIdle */
  510.  
  511.  
  512. //-----------------------------------------------------------------------
  513. // TEDocument::DoIdle -    This is called whenever we get a null event et al.
  514. //                         It takes care of necessary periodic actions. For
  515. //                         this program, it calls TEIdle.
  516. //
  517.     void TEDocument::DoIdle( void )
  518.     {
  519.         TEIdle( fDocTE );
  520.         
  521.     } /* TEDocument::DoIdle */
  522.  
  523.  
  524. //-----------------------------------------------------------------------
  525. // TEDocument::DrawWindow -    Draw the contents of an application window.
  526. //
  527.     void TEDocument::DrawWindow(void)
  528.     {
  529.         Rect tRect;
  530.     
  531.         SetPort( fDocWindow );
  532.         tRect = fDocWindow->portRect;
  533.         EraseRect( &tRect );
  534.         TEUpdate( &tRect, fDocTE );
  535.         DrawControls( fDocWindow );
  536.         DrawGrowIcon( fDocWindow );
  537.         
  538.     } /* TEDocument::DrawWindow */
  539.  
  540.  
  541. //-----------------------------------------------------------------------
  542. // TEDocument::GetTERect -    Return a rectangle that is inset from the portRect
  543. //                             by the size of the scrollbars and a little extra margin.
  544. //
  545.     void TEDocument::GetTERect( Rect* teRect )
  546.     {
  547.         *teRect = fDocWindow->portRect;
  548.         InsetRect( teRect, kTextMargin, kTextMargin );            // adjust for margin 
  549.         teRect->bottom = teRect->bottom - kScrollbarAdjust;        // and for the scrollbars 
  550.         teRect->right = teRect->right - kScrollbarAdjust;
  551.         
  552.     } /* TEDocument::GetTERect */
  553.  
  554.  
  555. //-----------------------------------------------------------------------
  556. // TEDocument::GetVisTERgn -    setup a region which contains the visible text
  557. //
  558.     void TEDocument::GetVisTERgn(RgnHandle rgn)
  559.     {
  560.         Rect teRect;
  561.     
  562.         teRect = (*fDocTE)->viewRect;                            // get a local copy of viewRect
  563.         SetPort(fDocWindow);                                    // make sure we have right port
  564.         LocalToGlobal(&TopLeft(teRect));
  565.         LocalToGlobal(&BotRight(teRect));
  566.         RectRgn(rgn, &teRect);
  567.  
  568.         // we temporarily change the port’s origin to “globalfy” the visRgn
  569.             SetOrigin( -( fDocWindow->portBits.bounds.left ), -( fDocWindow->portBits.bounds.top ));
  570.             SectRgn(rgn, fDocWindow->visRgn, rgn );
  571.             SetOrigin( 0, 0 );
  572.             
  573.     } /* TEDocument::GetVisTERgn */
  574.  
  575.  
  576. //-----------------------------------------------------------------------
  577. // TEDocument::HaveSelection -    Return boolean value indicating that there is
  578. //                                 or is not a selection in the document
  579. //
  580.     Boolean TEDocument::HaveSelection()
  581.     {
  582.         if ((*fDocTE)->selStart < (*fDocTE)->selEnd )
  583.             return true;
  584.         else
  585.             return false;
  586.             
  587.     } /* TEDocument::HaveSelection */
  588.  
  589.  
  590. //-----------------------------------------------------------------------
  591. // TEDocument::AdjustViewRect -    Update the TERec's view rect so that it is the
  592. //                                 greatest multiple ofthe line Height that still
  593. //                                fits in the old viewRect.
  594. //
  595.     void TEDocument::AdjustViewRect(void)
  596.     {
  597.         TEPtr te;
  598.         
  599.         te = *fDocTE;
  600.         te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
  601.                                 * te->lineHeight) + te->viewRect.top;
  602.                                 
  603.     } /* TEDocument::AdjustViewRect */
  604.  
  605.  
  606. //-----------------------------------------------------------------------
  607. // TEDocument::AdjustTE -    Scroll the TERec around to match up to the potentially
  608. //                             updated scrollbar values. This is really useful when
  609. //                             the window has been resized such that the scrollbars
  610. //                            became inactive but the TERec was already scrolled.
  611. //
  612.     void TEDocument::AdjustTE( void )
  613.     {
  614.         TEPtr te;
  615.         
  616.         te = *fDocTE;
  617.         TEScroll(( te->viewRect.left - te->destRect.left ) - GetControlValue( fDocHScroll ),
  618.                  ( te->viewRect.top - te->destRect.top ) -  (GetControlValue( fDocVScroll ) * te->lineHeight ),
  619.                  fDocTE );
  620.                  
  621.     } /* TEDocument::AdjustTE */
  622.  
  623.  
  624. //-----------------------------------------------------------------------
  625. // TEDocument::AdjustScrollSizes -    Re-calculate the position and size of the
  626. //                                     viewRect and the scrollbars. kScrollTweek
  627. //                                    compensates for off-by-one requirements of
  628. //                                     the scrollbars to have borders coincide
  629. //                                    with the growbox.
  630. //
  631.     void TEDocument::AdjustScrollSizes(void)
  632.     {
  633.         Rect teRect;
  634.         
  635.         GetTERect( &teRect );
  636.         (*fDocTE)->viewRect = teRect;
  637.         AdjustViewRect();
  638.         MoveControl( fDocVScroll, fDocWindow->portRect.right - kScrollbarAdjust, -1 );
  639.         SizeControl( fDocVScroll, kScrollbarWidth,
  640.                      fDocWindow->portRect.bottom - fDocWindow->portRect.top - kGrowboxAdjust + kScrollTweek );
  641.         MoveControl(fDocHScroll, -1, fDocWindow->portRect.bottom - kScrollbarAdjust);
  642.         SizeControl(fDocHScroll,
  643.                     fDocWindow->portRect.right - fDocWindow->portRect.left - kGrowboxAdjust + kScrollTweek,
  644.                     kScrollbarWidth);
  645.                     
  646.     } /* TEDocument::AdjustScrollSizes */
  647.  
  648.  
  649. //-----------------------------------------------------------------------
  650. // TEDocument::AdjustScrollbars -    Turn off the controls by jamming a zero into
  651. //                                    their contrlVis fields (HideControl erases them
  652. //                                    and we don't want that). If the controls are to
  653. //                                    be resized as well, call the procedure to do that,
  654. //                                    then call the procedure to adjust the maximum and
  655. //                                    current values. Finally re-enable the controls by
  656. //                                    jamming a $FF in their contrlVis fields (ShowControl
  657. //                                    re-draws the control, which may not be necessary).
  658. //
  659.     void TEDocument::AdjustScrollbars( Boolean needsResize )
  660.     {
  661.         // First, turn visibility of scrollbars off so we won’t get unwanted redrawing 
  662.             (*fDocVScroll)->contrlVis = 0;
  663.             (*fDocHScroll)->contrlVis = 0;
  664.             if ( needsResize )
  665.                 AdjustScrollSizes();
  666.             AdjustScrollValues( needsResize );
  667.             
  668.         // Now, restore visibility in case we never had to draw during adjustment 
  669.             (*fDocVScroll)->contrlVis = 0xff;
  670.             (*fDocHScroll)->contrlVis = 0xff;
  671.         
  672.     } /* TEDocument::AdjustScrollbars */
  673.  
  674. // Calculate the new control maximum value and current value, whether it is the horizontal or
  675.  
  676. //-----------------------------------------------------------------------
  677. // TEDocument::AdjustHV -    Turn off the controls by jamming a zero into vertical
  678. //                            scrollbar. The vertical max is calculated by comparing
  679. //                            the number of lines to the vertical size of the viewRect.
  680. //                            The horizontal max is calculated by comparing the
  681. //                            maximum document width to the width of the viewRect.
  682. //                            The current values are set by comparing the offset
  683. //                            between the view and destination rects. If necessary,
  684. //                            redraw the control by calling ShowControl.
  685. //
  686.     void TEDocument::AdjustHV( Boolean isVert, Boolean mustRedraw )
  687.     {
  688.             short value, lines, max;
  689.             short oldValue, oldMax;
  690.             TEPtr te;
  691.             ControlHandle control;
  692.         
  693.             if (isVert)
  694.                 control = fDocVScroll;
  695.             else
  696.                 control = fDocHScroll;
  697.             oldValue = GetControlValue( control );
  698.             oldMax = GetControlMaximum( control );
  699.             te = *fDocTE;                            // point to TERec for convenience 
  700.             if ( isVert )
  701.             {
  702.                 lines = te->nLines;
  703.                 // since nLines isn’t right if the last character is a return, check for that case
  704.                     if ( *(*te->hText + te->teLength - 1) == kCrChar )
  705.                         lines += 1;
  706.                     max = lines - (( te->viewRect.bottom - te->viewRect.top ) / te->lineHeight);
  707.             }
  708.             else
  709.                 max = kMaxDocWidth - ( te->viewRect.right - te->viewRect.left );
  710.             
  711.             if ( max < 0 )
  712.                 max = 0;
  713.             SetControlMaximum( control, max );
  714.             
  715.             // Must deref. after SetControlMaximum since, technically, it could draw and therefore move
  716.             // memory. This is why we don’t just do it once at the beginning.
  717.                 te = *fDocTE;
  718.                 if ( isVert )
  719.                   value = ( te->viewRect.top - te->destRect.top ) / te->lineHeight;
  720.                 else value = te->viewRect.left - te->destRect.left;
  721.                 
  722.                 if ( value < 0 )
  723.                     value = 0;
  724.                 else
  725.                     if ( value >  max )
  726.                         value = max;
  727.                 
  728.                 SetControlValue( control, value );
  729.                 
  730.             // now redraw the control if asked to or if a setting changed 
  731.                 if ( mustRedraw || ( max != oldMax ) || ( value != oldValue ))
  732.                     ShowControl( control );
  733.                 
  734.     } /* TEDocument::AdjustHV */
  735.  
  736.  
  737. //-----------------------------------------------------------------------
  738. // TEDocument::AdjustScrollValues -    Simply call the common adjust routine for
  739. //                                    the vertical and horizontal scrollbars.
  740. //
  741.     void TEDocument::AdjustScrollValues( Boolean mustRedraw )
  742.     {
  743.         AdjustHV( true, mustRedraw );
  744.         AdjustHV( false, mustRedraw );
  745.         
  746.     } /* TEDocument::AdjustScrollValues */
  747.  
  748.  
  749. //-----------------------------------------------------------------------
  750. // TEDocument::GetClickLoop -    
  751. //
  752.     TEClickLoopUPP TEDocument::GetClickLoop( void )
  753.     {
  754.         return fDocClick;
  755.         
  756.     } /* TEDocument::GetClickLoop */
  757.  
  758.  
  759. //-----------------------------------------------------------------------
  760. // TEDocument::GetTEHandle -    
  761. //
  762.     TEHandle TEDocument::GetTEHandle( void )
  763.     {
  764.         return fDocTE;
  765.         
  766.     } /* TEDocument::GetTEHandle */
  767.  
  768.  
  769. //-----------------------------------------------------------------------
  770. // TEDocument::DoCut -    
  771. //
  772.     void TEDocument::DoCut( void )
  773.     {
  774.         long total, contig;
  775.     
  776.         if ( ZeroScrap() == noErr )
  777.         {
  778.             PurgeSpace(&total, &contig);
  779.             if ((*fDocTE)->selEnd - (*fDocTE)->selStart + kTESlop > contig )
  780.                 AlertUser( kTEDocErrStrings,eNoSpaceCut );
  781.             else
  782.             {
  783.                 TECut( fDocTE );
  784.                 if ( TEToScrap() != noErr )
  785.                 {
  786.                     AlertUser( kTEDocErrStrings, eNoCut );
  787.                     (void) ZeroScrap();
  788.                 }
  789.             }
  790.         }
  791.         AdjustScrollbars(false);
  792.         AdjustTE();
  793.         
  794.     } /* TEDocument::DoCut */
  795.  
  796.  
  797. //-----------------------------------------------------------------------
  798. // TEDocument::DoCopy -    
  799. //
  800.     void TEDocument::DoCopy( void )
  801.     {
  802.         if ( ZeroScrap() == noErr )
  803.         {
  804.             TECopy(fDocTE);                        // after copying, export the TE scrap
  805.             if ( TEToScrap() != noErr )
  806.             {
  807.                 AlertUser(kTEDocErrStrings,eNoCopy);
  808.                 ZeroScrap();
  809.             }
  810.         }
  811.         AdjustScrollbars(false);
  812.         AdjustTE();
  813.         
  814.     } /* TEDocument::DoCopy */
  815.  
  816.  
  817. //-----------------------------------------------------------------------
  818. // TEDocument::DoPaste -    
  819. //
  820.     void TEDocument::DoPaste( void )
  821.     {
  822.         Handle aHandle;
  823.         long oldSize, newSize;
  824.         OSErr saveErr;
  825.     
  826.         if ( TEFromScrap() == noErr )
  827.         {
  828.             if ( TEGetScrapLength() + ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart )) > kMaxTELength )
  829.                 AlertUser( kTEDocErrStrings, eExceedPaste );
  830.             else
  831.             {
  832.                 aHandle = (Handle) TEGetText( fDocTE );
  833.                 oldSize = GetHandleSize( aHandle );
  834.                 newSize = oldSize + TEGetScrapLength() + kTESlop;
  835.                 SetHandleSize( aHandle, newSize );
  836.                 saveErr = MemError();
  837.                 SetHandleSize( aHandle, oldSize );
  838.                 
  839.                 if (saveErr != noErr)
  840.                     AlertUser( kTEDocErrStrings, eNoSpacePaste );
  841.                 else
  842.                     TEPaste(fDocTE);
  843.             }
  844.         }
  845.         else
  846.             AlertUser( kTEDocErrStrings, eNoPaste );
  847.             
  848.         AdjustScrollbars( false );
  849.         AdjustTE();
  850.         
  851.     } /* TEDocument::DoPaste */
  852.  
  853.  
  854. //-----------------------------------------------------------------------
  855. // TEDocument::DoClear -    
  856. //
  857.     void TEDocument::DoClear( void )
  858.     {
  859.         TEDelete( fDocTE );
  860.         AdjustScrollbars( false );
  861.         AdjustTE();
  862.         
  863.     } /* TEDocument::DoClear */
  864.  
  865.  
  866.  
  867. /***********************************************************************/
  868. //
  869. // Global declarations
  870. //            Routines used by this class, which don't belong to the class since we use
  871. //            them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
  872. //
  873. /***********************************************************************/
  874.  
  875.  
  876. //-----------------------------------------------------------------------
  877. // CommonAction -    Common algorithm for pinning the value of a control.
  878. //                    It returns the actual amount the value of the control
  879. //                    changed.
  880. //
  881.     void CommonAction( ControlHandle control, short* amount )
  882.     {
  883.         short        value, max;
  884.         
  885.         value = GetControlValue( control );
  886.         max = GetControlMaximum( control );
  887.         *amount = value - *amount;
  888.         
  889.         if ( *amount <= 0 )
  890.             *amount = 0;
  891.         else
  892.             if ( *amount >= max )
  893.                 *amount = max;
  894.                 
  895.         SetControlValue(control, *amount);
  896.         *amount = value - *amount;
  897.         
  898.     } /* CommonAction */
  899.  
  900.  
  901. //-----------------------------------------------------------------------
  902. // VActionProc -    Determines how much to change the value of the vertical
  903. //                     scrollbar by and how much to scroll the TE record.
  904. //
  905.     pascal void VActionProc( ControlHandle control, short part )
  906.     {
  907.         short        amount;
  908.         WindowPtr    window;
  909.         TEPtr        te;
  910.         TEDocument* doc;
  911.     
  912.         if ( part != 0 )
  913.         {
  914.             window = (*control)->contrlOwner;
  915.             doc = GetWindowDoc( window );
  916.             te = *(doc->GetTEHandle() );
  917.             switch ( part )
  918.             {
  919.                 case kControlUpButtonPart:                    // one line
  920.                 case kControlDownButtonPart:
  921.                                         amount = 1;                         
  922.                                         break;
  923.                                         
  924.                 case kControlPageUpPart:                    // one page 
  925.                 case kControlPageDownPart:
  926.                                         amount = (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
  927.                                         break;
  928.             }
  929.               
  930.             if (( part == kControlDownButtonPart ) || ( part == kControlPageDownPart ))
  931.                 amount = -amount;                            // reverse direction for a downer 
  932.             CommonAction( control, &amount );
  933.             
  934.             if ( amount != 0 )
  935.                 TEScroll( 0, amount * te->lineHeight, doc->GetTEHandle());
  936.         }
  937.     } /* VActionProc */ 
  938.  
  939.  
  940. //-----------------------------------------------------------------------
  941. // HActionProc -    Determines how much to change the value of the horizontal
  942. //                     scrollbar by and how much to scroll the TE record.
  943. //
  944.     pascal void HActionProc( ControlHandle control, short part )
  945.     {
  946.         short        amount;
  947.         WindowPtr    window;
  948.         TEPtr        te;
  949.         TEDocument* doc;
  950.     
  951.         if ( part != 0 )
  952.         {
  953.             window = (*control)->contrlOwner;
  954.             doc = GetWindowDoc( window );
  955.             te = *(doc->GetTEHandle());
  956.             switch ( part )
  957.             {
  958.                 case kControlUpButtonPart:                    // a few pixels
  959.                 case kControlDownButtonPart:
  960.                                         amount = 4;
  961.                                         break;
  962.                                         
  963.                 case kControlPageUpPart:                    // a page 
  964.                 case kControlPageDownPart:
  965.                                         amount = te->viewRect.right - te->viewRect.left;
  966.                                         break;
  967.             }
  968.             
  969.             if (( part == kControlDownButtonPart ) || ( part == kControlPageDownPart ))
  970.                 amount = -amount;                            // reverse direction 
  971.             CommonAction( control, &amount );
  972.             
  973.             if ( amount != 0 )
  974.                 TEScroll( amount, 0, doc->GetTEHandle());
  975.         }
  976.     } /* HActionProc */
  977.  
  978.  
  979. //-----------------------------------------------------------------------
  980. // PascalClickLoop -Gets called from our assembly language routine (on the
  981. //                  68K, C on the PowerPC), ASMCLICKLOOP,
  982. //                    which is in turn called by the TEClick toolbox routine.
  983. //                    Saves the windows clip region, sets it to the portRect,
  984. //                    adjusts the scrollbar values to match the TE scroll amount,
  985. //                    then restores the clip region.
  986. //
  987.     pascal void PascalClickLoop(void)
  988.     {
  989.         RgnHandle    region;
  990.         WindowPtr wind;
  991.         TEDocument* doc;
  992.     
  993.         wind = FrontWindow();
  994.         doc = GetWindowDoc( wind );
  995.         region = NewRgn();
  996.         GetClip( region );                        // save clip 
  997.         ClipRect( &wind->portRect );
  998.         doc->AdjustScrollValues( false );
  999.         SetClip( region );                        // restore clip 
  1000.         DisposeRgn( region );
  1001.         
  1002.     } /* PascalClickLoop */
  1003.  
  1004.  
  1005. //-----------------------------------------------------------------------
  1006. // GetOldClickLoop -Gets called from our assembly language routine (on the
  1007. //                  68K, C on the PowerPC), ASMCLICKLOOP,
  1008. //                    which is in turn called by the TEClick toolbox routine. It
  1009. //                    returns the address of the default ClickLoop routine that was
  1010. //                    put into the TERec by TEAutoView to ASMCLICKLOOP so that it
  1011. //                    can call it.
  1012. //
  1013.    pascal TEClickLoopUPP GetOldClickLoop(void)
  1014.     {
  1015.         TEDocument* doc;
  1016.     
  1017.         doc = GetWindowDoc( FrontWindow());
  1018.         if (doc == nil)
  1019.             return nil;
  1020.             
  1021.         return doc->GetClickLoop();
  1022.         
  1023.     } /* GetOldClickLoop */
  1024.  
  1025.  
  1026. //-----------------------------------------------------------------------
  1027. // AlertUser -    
  1028. //
  1029.     void AlertUser( short errResID, short errCode )
  1030.     {
  1031.         Str255 message;
  1032.     
  1033.         SetCursor( &qd.arrow );
  1034.         GetIndString( message, errResID, errCode );
  1035.         ParamText( message,(ConstStr255Param) "\p", (ConstStr255Param)"\p", (ConstStr255Param)"\p" );
  1036.         (void) Alert( rUserAlert,  nil );
  1037.         
  1038.     } /* AlertUser */
  1039.  
  1040. #if GENERATINGCFM
  1041. //------------------------------------------------------------------------
  1042. // ASMCLICKLOOP - On the 68K we have to have an assembler routine do this
  1043. //               because registers must be mucked with.  On the PowerPC
  1044. //               we have MixedMode which gives us a C interface to a
  1045. //               TEClickLoopProc ();
  1046. //
  1047. //               Our job is to 1) Call the old, default click loop routine
  1048. //               that scrolls text. 2) Call our own routine that handles
  1049. //               tracking the scroll bars to follow along.
  1050. //
  1051.      pascal Boolean ASMCLICKLOOP (TEPtr pTE)
  1052.       {
  1053.          TEClickLoopUPP Old_ClickLoopRoutine;
  1054.             
  1055.          Old_ClickLoopRoutine = GetOldClickLoop ();
  1056.          CallTEClickLoopProc (Old_ClickLoopRoutine, pTE);
  1057.          
  1058.          CallUniversalProc((UniversalProcPtr)(Old_ClickLoopRoutine), uppTEClickLoopProcInfo, pTE);
  1059.          
  1060.          PascalClickLoop ();
  1061.          
  1062.          return(true);
  1063.       
  1064.       } /* ASMCLICKLOOP */
  1065.  
  1066. #endif